const isObject = (value) => value !== null && typeof value === "object"
const hasOwnProperty = Object.prototype.hasOwnProperty
// 判断 target 是否具有 key 属性
const hasOwn = (target, key) => hasOwnProperty.call(target, key)

const reactive = (target) => {
	if (!isObject(target)) return target

	const handler = {
		get(target, key, receiver) {
			const result = Reflect.get(target, key)
			// 触发依赖收集
			track(target, key)
			return reactive(result)
		},
		set(target, key, value, receiver) {
			const oldValue = Reflect.get(target, key)
			if (value !== oldValue) {
				const result = Reflect.set(target, key, value)
				// 触发依赖更新
				trigger(target, key)
				return result
			}
		},
		deleteProperty(target, key) {
			const hasKey = hasOwn(target, key)
			const result = Reflect.deleteProperty(target, key)
			if (hasKey && result) {
				// 触发依赖更新
				trigger(target, key)
			}
		},
	}
	return new Proxy(target, handler)
}

let activeFn = null

const effect = (fn) => {
	activeFn = fn
	fn()
	activeFn = null
}

const targetMap = new WeakMap()
// 收集依赖函数
const track = (target, key) => {
	// activeFn 为空，证明没有依赖要收集
	if (!activeFn) return

	let desMap = targetMap.get(target)
	if (!desMap) targetMap.set(target, (desMap = new Map()))

	let dep = desMap.get(key)
	// 这里用 Set 的数据结构是因为在一个 effect 中，一个响应式对象的属性可以被多次调用
	if (!dep) desMap.set(key, (dep = new Set()))
	dep.add(activeFn)
}

const trigger = (target, key) => {
	let desMap = targetMap.get(target)
	if (!desMap) return
	let dep = desMap.get(key)
	if (!dep) return
	dep.forEach((fn) => {
		fn()
	})
}

export { reactive, effect }
